package com.base.vistter.iframe.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.base.vistter.iframe.entity.BaseEntity;
import com.base.vistter.iframe.exception.IframeException;
import jakarta.annotation.PostConstruct;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;

@Transactional
public class BaseService<M extends BaseMapper<T>, T extends BaseEntity> {

    protected Log log = LogFactory.getLog(this.getClass());

    public static final Integer DEFAULT_BATCH_SIZE = 1000;

    public static final String PARENT_PATH_DIVIDE = ",";

    @Autowired
    private ApplicationContext applicationContext;

    protected M baseMapper;

    protected M getBaseMapper() {
        return this.baseMapper;
    }

    protected Class<T> entityClass = this.currentModelClass();
    protected Class<M> mapperClass = this.currentMapperClass();

    @PostConstruct
    protected void init() {
        Type genType = this.getClass().getGenericSuperclass();
        if (genType instanceof ParameterizedType) {
            Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
            if (null != params && params.length > 0) {
                System.out.println(this.applicationContext);
                this.baseMapper = (M) this.applicationContext.getBean((Class) params[0]);
            }
        }
    }

    protected Class<M> currentMapperClass() {
        return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseService.class, 0);
    }

    protected Class<T> currentModelClass() {
        return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseService.class, 1);
    }

    /**
     * @param page    分页对象
     * @param wrapper 分页条件
     * @param <E>
     * @return
     * @throws IframeException
     */
    @Transactional(readOnly = true)
    public <E extends IPage<T>> E page(E page, Wrapper<T> wrapper) throws IframeException {
        try {
            return getBaseMapper().selectPage(page, wrapper);
        } catch (RuntimeException e) {
            log.error(e.getMessage());
            throw new IframeException(e.getMessage());
        }
    }

    /**
     * @param page 分页对象
     * @param <E>
     * @return
     * @throws IframeException
     */
    @Transactional(readOnly = true)
    public <E extends IPage<T>> E page(E page) throws IframeException {
        try {
            return this.page(page, Wrappers.emptyWrapper());
        } catch (RuntimeException e) {
            log.error(e.getMessage());
            throw new IframeException(e.getMessage());
        }
    }

    /**
     * @param id 唯一标识
     * @return
     * @throws IframeException
     */
    @Transactional(readOnly = true)
    public T getById(Serializable id) throws IframeException {
        try {
            return getBaseMapper().selectById(id);
        } catch (RuntimeException e) {
            log.error(e.getMessage());
            throw new IframeException(e.getMessage());
        }
    }

    /**
     * @return
     * @throws IframeException
     */
    @Transactional(readOnly = true)
    public List<T> selectList() throws IframeException {
        return this.selectList(Wrappers.emptyWrapper());
    }

    /**
     * @return
     * @throws IframeException
     */
    @Transactional(readOnly = true)
    public List<T> selectList(Wrapper<T> wrapper) throws IframeException {
        try {
            return this.getBaseMapper().selectList(wrapper);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new IframeException(e.getMessage());
        }
    }

    @Transactional(readOnly = true)
    public List<Map<String, Object>> selectMaps(Wrapper<T> queryWrapper) throws IframeException {
        try {
            return this.getBaseMapper().selectMaps(queryWrapper);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new IframeException(e.getMessage());
        }
    }

    @Transactional(readOnly = true)
    public List<Map<String, Object>> selectMaps() throws IframeException {
        return this.selectMaps(Wrappers.emptyWrapper());
    }

    @Transactional(readOnly = true)
    public T selectOne() throws IframeException {
        return this.selectOne(Wrappers.emptyWrapper());
    }

    /**
     * @return
     * @throws IframeException
     */
    @Transactional(readOnly = true)
    public T selectOne(Wrapper<T> wrapper) throws IframeException {
        try {
            return this.getBaseMapper().selectOne(wrapper);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new IframeException(e.getMessage());
        }
    }

    @Transactional(readOnly = true)
    public boolean exists(Wrapper<T> wrapper) throws IframeException {
        Long count = this.selectCount(wrapper);
        return null != count && count.intValue() > 0;
    }

    @Transactional(readOnly = true)
    public Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> wrapper) throws IframeException {
        try {
            return this.getBaseMapper().selectCount(wrapper);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new IframeException(e.getMessage());
        }
    }

    @Transactional(readOnly = true)
    public Long selectCount() throws IframeException {
        try {
            return this.selectCount(Wrappers.emptyWrapper());
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new IframeException(e.getMessage());
        }
    }

    /**
     * 保存实体
     *
     * @return
     * @throws IframeException
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public boolean save(T entity) throws IframeException {
        return this.retBool(this.getBaseMapper().insert(entity));
    }

    public boolean saveBatch(Collection<T> entityList) {
        String sqlStatement = this.getSqlStatement(SqlMethod.INSERT_ONE);
        return this.executeBatch(entityList, DEFAULT_BATCH_SIZE, (sqlSession, entity) -> {
            sqlSession.insert(sqlStatement, entity);
        });
    }

    protected String getSqlStatement(SqlMethod sqlMethod) {
        return SqlHelper.getSqlStatement(this.mapperClass, sqlMethod);
    }

    protected boolean executeBatch(Collection<T> list, int batchSize, BiConsumer<SqlSession, T> consumer) {
        return SqlHelper.executeBatch(this.entityClass, log, list, batchSize, consumer);
    }

    /**
     * 修改对象
     *
     * @param entity
     * @return
     * @throws IframeException
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public boolean update(T entity) throws IframeException {
        return this.updateById(entity);
    }

    /**
     * 通过ID修改对象
     *
     * @param entity
     * @return
     * @throws IframeException
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public boolean updateById(T entity) throws IframeException {
        return this.retBool(this.getBaseMapper().updateById(entity));
    }

    /**
     * @param entity
     * @param wrapper
     * @return
     * @throws IframeException
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public boolean update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> wrapper) throws IframeException {
        return this.retBool(this.getBaseMapper().update(entity, wrapper));
    }

    /**
     * 通过主键删除对象
     *
     * @param id
     * @return
     * @throws IframeException
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public int delete(Serializable id) throws IframeException {
        try {
            return this.getBaseMapper().deleteById(id);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new IframeException(e.getMessage());
        }
    }

    /**
     * 通过对象删除对象
     *
     * @param entity
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public int delete(T entity) throws IframeException {
        try {
            return this.delete(entity.getId());
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new IframeException(e.getMessage());
        }
    }

    /**
     * 通过Map集合删除对象
     *
     * @param columnMap
     * @return
     * @throws IframeException
     */
    public int delete(Map<String, Object> columnMap) throws IframeException {
        try {
            return this.getBaseMapper().deleteByMap(columnMap);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new IframeException(e.getMessage());
        }
    }

    /**
     * 构造条件删除对象
     *
     * @param wrapper
     * @return
     * @throws IframeException
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public int delete(Wrapper<T> wrapper) throws IframeException {
        try {
            return this.getBaseMapper().delete(wrapper);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new IframeException(e.getMessage());
        }
    }

    /**
     * 通过多个主键删除对象
     *
     * @param idList
     * @return
     * @throws IframeException
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public int delete(Collection<?> idList) throws IframeException {
        if (CollectionUtils.isEmpty(idList)) {
            return 0;
        }
        try {
            return this.getBaseMapper().deleteBatchIds(idList);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new IframeException(e.getMessage());
        }
    }

    /**
     * 构建响应结果成功失败
     *
     * @param result
     * @return
     * @throws IframeException
     */
    protected boolean retBool(Integer result) throws IframeException {
        if (null != result && result >= 1) {
            return true;
        } else {
            throw new IframeException("save or update fail");
        }
    }

}
